/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


/*************************************************************************
 * File: buffer.h
 *************************************************************************
 *\author: starlove
 *************************************************************************/

#ifndef __MLSE_LIBRARY_BUFFER_H__
#define __MLSE_LIBRARY_BUFFER_H__

#include <tools/mutex.h>
#include <tools/debug.h>

#define __MLSE_AUTOBUFFER_DEFSIZE__ 4096

template <int DEFSIZE>
class CAutoExpandBuffer : public CSemiAutoIntLock
{
public:
    CAutoExpandBuffer()
        :m_szExpand(NULL)
        ,m_curr(NULL)
        ,m_ptr(NULL)
        ,m_size(DEFSIZE)
        ,m_bDamaged(false)
    {
        Reset();
    }

    char *GetRawBuffer()
    {
        return m_szRawBuff;
    }

    char *GetCurrPtr()
    {
        return m_curr;
    }

    char *GetDataEnd()
    {
        return m_curr > m_ptr ? m_curr - 1: NULL;
    }

    char *GetPtr()
    {
        return m_ptr;
    }

    void SetOffset(unsigned int size)
    {
		AUTO_SELF_MUTEX(push_mutex);
        if(size > m_size)
            return;
        m_curr += size;
    }

    unsigned int GetSize()
    {
        return m_size;
    }

    unsigned int GetDataSize()
    {
        return (m_curr - m_ptr);
    }

    unsigned int GetRemainSize()
    {
    	return m_size - GetDataSize();
    }

    bool Align(unsigned int nSize)
    {
		AUTO_SELF_MUTEX(push_mutex);
		if(nSize == 0)	return false;
        unsigned int ndata_size = (m_curr - m_ptr);
        if(ndata_size <= nSize)
        {
			Reset();
			return false;
        }
        else
        {
        	unsigned int n_newsize = ndata_size - nSize;
			char *sz_new = (char *)(m_ptr + nSize);
			memcpy(m_ptr, sz_new, n_newsize);

			m_curr = (m_ptr + n_newsize);
			//DEBUG_INFO_LIBRARY("buffer align suscess. newsize: %d, oldsize: %d, alignsize: %d",n_newsize , ndata_size, nSize);
			return true;
        }
    }

    bool Expand(unsigned int nSize)
    {
        if(nSize <= m_size)
            return false;
        m_szExpand = (char *)malloc(nSize);
        if (!m_szExpand)
            return false;
        unsigned int nDataSize = m_curr - m_ptr;
        char *oldptr = m_ptr;
        memcpy(m_szExpand, m_ptr, nDataSize);
        m_curr = m_szExpand + nDataSize;
        m_ptr = m_szExpand;

        if (oldptr != m_szRawBuff)
        {
            ::free(oldptr);
        }
        m_size = nSize;
        return true;
    }

    void Reset()
    {
        if(m_szExpand)
        {
            ::free(m_szExpand);
        }
        m_ptr = m_curr = m_szRawBuff;
        m_size = DEFSIZE;
    }

    bool CopyFrom(void *p, unsigned int nsize)
    {
		AUTO_SELF_MUTEX(push_mutex);
        if( !Expand(GetDataSize() + nsize) ) return false;
        memcpy(m_ptr, p, nsize);
        m_curr = m_ptr + nsize;
        return true;
    }

    void Append(const void *p, unsigned int nsize)
    {
		AUTO_SELF_MUTEX(push_mutex);
        if(GetDataSize() + nsize > m_size)
			return;
		memcpy(m_curr, p, nsize);
        m_curr += nsize;
    }

	bool IsDamaged(){ return m_bDamaged; }
private:
    char m_szRawBuff[DEFSIZE];
    char *m_szExpand;
    char *m_curr;
    char *m_ptr;
    unsigned int m_size;
    bool m_bDamaged;
};

#endif // __MLSE_LIBRARY_BUFFER_H__
